/*
 * drivers/pwm/pwm-sunxi.c
 *
 * Allwinnertech pulse-width-modulation controller driver
 *
 * Copyright (C) 2015 AllWinner
 *
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2. This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 */
#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/pwm.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-sunxi.h>
#include <linux/pinctrl/consumer.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/of_iommu.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>

#include <linux/io.h>

#define PWM_DEBUG 0
#define PWM_NUM_MAX 4
#define PWM_BIND_NUM 2
#define PWM_PIN_STATE_ACTIVE "active"
#define PWM_PIN_STATE_SLEEP "sleep"

#define SETMASK(width, shift)   ((width?((-1U) >> (32-width)):0)  << (shift))
#define CLRMASK(width, shift)   (~(SETMASK(width, shift)))
#define GET_BITS(shift, width, reg)     \
            (((reg) & SETMASK(width, shift)) >> (shift))
#define SET_BITS(shift, width, reg, val) \
            (((reg) & CLRMASK(width, shift)) | (val << (shift)))

#if PWM_DEBUG
#define pwm_debug(msg...) pr_info
#else
#define pwm_debug(msg...)
#endif

struct sunxi_pwm_config {
	unsigned int reg_peci_offset;
	unsigned int reg_peci_shift;
	unsigned int reg_peci_width;

	unsigned int reg_pis_offset;
	unsigned int reg_pis_shift;
	unsigned int reg_pis_width;

	unsigned int reg_crie_offset;
	unsigned int reg_crie_shift;
	unsigned int reg_crie_width;

	unsigned int reg_cfie_offset;
	unsigned int reg_cfie_shift;
	unsigned int reg_cfie_width;

	unsigned int reg_cris_offset;
	unsigned int reg_cris_shift;
	unsigned int reg_cris_width;

	unsigned int reg_cfis_offset;
	unsigned int reg_cfis_shift;
	unsigned int reg_cfis_width;

	unsigned int reg_clk_src_offset;
	unsigned int reg_clk_src_shift;
	unsigned int reg_clk_src_width;

	unsigned int reg_bypass_offset;
	unsigned int reg_bypass_shift;
	unsigned int reg_bypass_width;

	unsigned int reg_clk_gating_offset;
	unsigned int reg_clk_gating_shift;
	unsigned int reg_clk_gating_width;

	unsigned int reg_clk_div_m_offset;
	unsigned int reg_clk_div_m_shift;
	unsigned int reg_clk_div_m_width;

	unsigned int reg_pdzintv_offset;
	unsigned int reg_pdzintv_shift;
	unsigned int reg_pdzintv_width;

	unsigned int reg_dz_en_offset;
	unsigned int reg_dz_en_shift;
	unsigned int reg_dz_en_width;

	unsigned int reg_enable_offset;
	unsigned int reg_enable_shift;
	unsigned int reg_enable_width;

	unsigned int reg_cap_en_offset;
	unsigned int reg_cap_en_shift;
	unsigned int reg_cap_en_width;

	unsigned int reg_period_rdy_offset;
	unsigned int reg_period_rdy_shift;
	unsigned int reg_period_rdy_width;

	unsigned int reg_pul_start_offset;
	unsigned int reg_pul_start_shift;
	unsigned int reg_pul_start_width;

	unsigned int reg_mode_offset;
	unsigned int reg_mode_shift;
	unsigned int reg_mode_width;

	unsigned int reg_act_sta_offset;
	unsigned int reg_act_sta_shift;
	unsigned int reg_act_sta_width;

	unsigned int reg_prescal_offset;
	unsigned int reg_prescal_shift;
	unsigned int reg_prescal_width;

	unsigned int reg_entire_offset;
	unsigned int reg_entire_shift;
	unsigned int reg_entire_width;

	unsigned int reg_active_offset;
	unsigned int reg_active_shift;
	unsigned int reg_active_width;
	unsigned int dead_time;
	unsigned int bind_pwm;

};

struct sunxi_pwm_chip {
	struct pwm_chip chip;
	void __iomem *base;
	struct sunxi_pwm_config *config;
};

static inline struct sunxi_pwm_chip *to_sunxi_pwm_chip(struct pwm_chip *chip)
{
	return container_of(chip, struct sunxi_pwm_chip, chip);
}

static inline u32 sunxi_pwm_readl(struct pwm_chip *chip, u32 offset)
{
	struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
	u32 value = 0;

	value = readl(pc->base + offset);

	return value;
}

static inline u32 sunxi_pwm_writel(struct pwm_chip *chip, u32 offset, u32 value)
{
	struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);

	writel(value, pc->base + offset);

	return 0;
}

static int sunxi_pwm_pin_set_state(struct device *dev, char *name)
{
	struct pinctrl *pctl;
	struct pinctrl_state *state;
	int ret = -1;

	pctl = pinctrl_get(dev);
	if (IS_ERR(pctl)) {
		dev_err(dev, "pinctrl_get failed!\n");
		ret = PTR_ERR(pctl);
		goto exit;
	}

	state = pinctrl_lookup_state(pctl, name);
	if (IS_ERR(state)) {
		dev_err(dev, "pinctrl_lookup_state(%s) failed!\n", name);
		ret = PTR_ERR(state);
		goto exit;
	}

	ret = pinctrl_select_state(pctl, state);
	if (ret < 0) {
		dev_err(dev, "pinctrl_select_state(%s) failed!\n", name);
		goto exit;
	}
	ret = 0;

exit:
	return ret;
}





static int sunxi_pwm_get_config(struct platform_device *pdev, struct sunxi_pwm_config *config)
{
	struct device_node *np = pdev->dev.of_node;
	int ret = 0;

	/* read register config */
	ret = of_property_read_u32(np, "reg_peci_offset", &config->reg_peci_offset);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_peci_offset! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_peci_shift", &config->reg_peci_shift);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_peci_shift! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_peci_width", &config->reg_peci_width);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_peci_width! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_pis_offset", &config->reg_pis_offset);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_pis_offset! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_pis_shift", &config->reg_pis_shift);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_pis_shift! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_pis_width", &config->reg_pis_width);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_pis_width! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_crie_offset", &config->reg_crie_offset);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_crie_offset! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_crie_shift", &config->reg_crie_shift);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_crie_shift! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_bypass_shift", &config->reg_bypass_shift);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_bypass_shift! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_crie_width", &config->reg_crie_width);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_crie_width! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_cfie_offset", &config->reg_cfie_offset);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_cfie_offset! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_cfie_shift", &config->reg_cfie_shift);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_cfie_shift! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_cfie_width", &config->reg_cfie_width);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_cfie_width! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_cris_offset", &config->reg_cris_offset);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_cris_offset! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_cris_shift", &config->reg_cris_shift);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_cris_shift! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_cris_width", &config->reg_cris_width);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_cris_width! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_cfis_offset", &config->reg_cfis_offset);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_cfis_offset! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_cfis_shift", &config->reg_cfis_shift);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_cfis_shift! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_cfis_width", &config->reg_cfis_width);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_cfis_width! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_clk_src_offset", &config->reg_clk_src_offset);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_clk_src_offset! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_clk_src_shift", &config->reg_clk_src_shift);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_clk_src_shift! err=%d\n", ret);
		goto err;
	}

	ret = of_property_read_u32(np, "reg_clk_src_width", &config->reg_clk_src_width);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_clk_src_width! err=%d\n", ret);
		goto err;
	}
	ret = of_property_read_u32(np, "reg_bypass_offset", &config->reg_bypass_offset);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_bypass_offset! err=%d\n", ret);
		goto err;
	}
	ret = of_property_read_u32(np, "reg_bypass_shift", &config->reg_bypass_shift);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_bypass_shift! err=%d\n", ret);
		goto err;
	}
	ret = of_property_read_u32(np, "reg_bypass_width", &config->reg_bypass_width);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_bypass_width! err=%d\n", ret);
		goto err;
	}
	ret = of_property_read_u32(np, "reg_clk_gating_offset", &config->reg_clk_gating_offset);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_clk_gating_offset! err=%d\n", ret);
		goto err;
	}
	ret = of_property_read_u32(np, "reg_clk_gating_shift", &config->reg_clk_gating_shift);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_clk_gating_shift! err=%d\n", ret);
		goto err;
	}
	ret = of_property_read_u32(np, "reg_clk_gating_width", &config->reg_clk_gating_width);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_clk_gating_width! err=%d\n", ret);
		goto err;
	}
	ret = of_property_read_u32(np, "reg_clk_div_m_offset", &config->reg_clk_div_m_offset);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_clk_div_m_offset! err=%d\n", ret);
		goto err;
	}
	ret = of_property_read_u32(np, "reg_clk_div_m_shift", &config->reg_clk_div_m_shift);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_clk_div_m_shift! err=%d\n", ret);
		goto err;
	}
	ret = of_property_read_u32(np, "reg_clk_div_m_width", &config->reg_clk_div_m_width);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_clk_div_m_width! err=%d\n", ret);
		goto err;
	}
	ret = of_property_read_u32(np, "reg_pdzintv_offset", &config->reg_pdzintv_offset);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_pdzintv_offset! err=%d\n", ret);
		goto err;
	}
	ret = of_property_read_u32(np, "reg_pdzintv_shift", &config->reg_pdzintv_shift);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_pdzintv_shift! err=%d\n", ret);
		goto err;
	}
	ret = of_property_read_u32(np, "reg_pdzintv_width", &config->reg_pdzintv_width);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_pdzintv_width! err=%d\n", ret);
		goto err;
	}
	ret = of_property_read_u32(np, "reg_dz_en_offset", &config->reg_dz_en_offset);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_dz_en_offset! err=%d\n", ret);
		goto err;
	}
	ret = of_property_read_u32(np, "reg_dz_en_shift", &config->reg_dz_en_shift);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_dz_en_shift! err=%d\n", ret);
		goto err;
	}
	ret = of_property_read_u32(np, "reg_dz_en_width", &config->reg_dz_en_width);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get reg_dz_en_width! err=%d\n", ret);
		goto err;
	}
	ret = of_property_read_u32(np, "reg_enable_offset", &config->reg_enable_offset);

	ret = of_property_read_u32(np, "reg_enable_shift", &config->reg_enable_shift);

	ret = of_property_read_u32(np, "reg_enable_width", &config->reg_enable_width);

	ret = of_property_read_u32(np, "reg_cap_en_offset", &config->reg_cap_en_offset);

	ret = of_property_read_u32(np, "reg_cap_en_shift", &config->reg_cap_en_shift);

	ret = of_property_read_u32(np, "reg_cap_en_width", &config->reg_cap_en_width);

	ret = of_property_read_u32(np, "reg_period_rdy_offset", &config->reg_period_rdy_offset);

	ret = of_property_read_u32(np, "reg_period_rdy_shift", &config->reg_period_rdy_shift);

	ret = of_property_read_u32(np, "reg_period_rdy_width", &config->reg_period_rdy_width);

	ret = of_property_read_u32(np, "reg_pul_start_offset", &config->reg_pul_start_offset);

	ret = of_property_read_u32(np, "reg_pul_start_shift", &config->reg_pul_start_shift);

	ret = of_property_read_u32(np, "reg_pul_start_width", &config->reg_pul_start_width);

	ret = of_property_read_u32(np, "reg_mode_offset", &config->reg_mode_offset);

	ret = of_property_read_u32(np, "reg_mode_shift", &config->reg_mode_shift);

	ret = of_property_read_u32(np, "reg_mode_width", &config->reg_mode_width);

	ret = of_property_read_u32(np, "reg_act_sta_offset", &config->reg_act_sta_offset);

	ret = of_property_read_u32(np, "reg_act_sta_shift", &config->reg_act_sta_shift);

	ret = of_property_read_u32(np, "reg_act_sta_width", &config->reg_act_sta_width);

	ret = of_property_read_u32(np, "reg_prescal_offset", &config->reg_prescal_offset);

	ret = of_property_read_u32(np, "reg_prescal_shift", &config->reg_prescal_shift);

	ret = of_property_read_u32(np, "reg_prescal_width", &config->reg_prescal_width);

	ret = of_property_read_u32(np, "reg_entire_offset", &config->reg_entire_offset);

	ret = of_property_read_u32(np, "reg_entire_shift", &config->reg_entire_shift);

	ret = of_property_read_u32(np, "reg_entire_width", &config->reg_entire_width);

	ret = of_property_read_u32(np, "reg_active_offset", &config->reg_active_offset);

	ret = of_property_read_u32(np, "reg_active_shift", &config->reg_active_shift);

	ret = of_property_read_u32(np, "reg_active_width", &config->reg_active_width);

	ret = of_property_read_u32(np, "bind_pwm", &config->bind_pwm);
	if (ret < 0) {
		/*if there is no bind pwm,set 255, dual pwm invalid!*/
		dev_err(&pdev->dev, "there is no bind_pwm, set bind_pwm 255!\n");
		config->bind_pwm = 255;
		ret = 0;
	}

	ret = of_property_read_u32(np, "dead_time", &config->dead_time);
	if (ret < 0) {
		/*if there is  bind pwm, but not set dead time,set bind pwm 255,dual pwm invalid!*/
		dev_err(&pdev->dev, "there is no dead time, set bind_pwm 255!\n");
		config->bind_pwm = 255;
		ret = 0;
	}

err:

	of_node_put(np);

	return ret;
}

static int sunxi_pwm_set_polarity_single(struct pwm_chip *chip, struct pwm_device *pwm, enum pwm_polarity polarity)
{
	u32 temp;
	struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
	unsigned int reg_offset, reg_shift, reg_width;

	reg_offset = pc->config[pwm->pwm - chip->base].reg_act_sta_offset;
	reg_shift = pc->config[pwm->pwm - chip->base].reg_act_sta_shift;
	reg_width = pc->config[pwm->pwm - chip->base].reg_act_sta_width;
	temp = sunxi_pwm_readl(chip, reg_offset);
	if (polarity == PWM_POLARITY_NORMAL)
		temp = SET_BITS(reg_shift, 1, temp, 1);
	else
		temp = SET_BITS(reg_shift, 1, temp, 0);

	sunxi_pwm_writel(chip, reg_offset, temp);

	return 0;
}

static int sunxi_pwm_set_polarity_dual(struct pwm_chip *chip, struct pwm_device *pwm, enum pwm_polarity polarity, int bind_num)
{
	u32 temp[2];
	struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
	unsigned int reg_offset[2], reg_shift[2], reg_width[2];


	/* config current pwm*/
	reg_offset[0] = pc->config[pwm->pwm - chip->base].reg_act_sta_offset;
	reg_shift[0] = pc->config[pwm->pwm - chip->base].reg_act_sta_shift;
	reg_width[0] = pc->config[pwm->pwm - chip->base].reg_act_sta_width;
	temp[0] = sunxi_pwm_readl(chip, reg_offset[0]);
	if (polarity == PWM_POLARITY_NORMAL)
		temp[0] = SET_BITS(reg_shift[0], 1, temp[0], 1);
	else
		temp[0] = SET_BITS(reg_shift[0], 1, temp[0], 0);

	/* config bind pwm*/
	reg_offset[1] = pc->config[bind_num - chip->base].reg_act_sta_offset;
	reg_shift[1] = pc->config[bind_num - chip->base].reg_act_sta_shift;
	reg_width[1] = pc->config[bind_num - chip->base].reg_act_sta_width;
	temp[1] = sunxi_pwm_readl(chip, reg_offset[1]);

	/*bind pwm's polarity is reverse compare with the  current pwm*/
	if (polarity == PWM_POLARITY_NORMAL)
		temp[1] = SET_BITS(reg_shift[0], 1, temp[1], 0);
	else
		temp[1] = SET_BITS(reg_shift[0], 1, temp[1], 1);

	/*config register at the same time*/
	sunxi_pwm_writel(chip, reg_offset[0], temp[0]);
	sunxi_pwm_writel(chip, reg_offset[1], temp[1]);

	return 0;

}

static int sunxi_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, enum pwm_polarity polarity)
{
	int bind_num;
	struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);

	bind_num = pc->config[pwm->pwm - chip->base].bind_pwm;
	if (bind_num == 255)
		sunxi_pwm_set_polarity_single(chip, pwm, polarity);
	else
		sunxi_pwm_set_polarity_dual(chip, pwm, polarity, bind_num);

	return 0;
}


#define PRESCALE_MAX 256

static int sunxi_pwm_config_single(struct pwm_chip *chip, struct pwm_device *pwm,
		int duty_ns, int period_ns)
{
	unsigned int temp;
	unsigned long long c = 0;
	unsigned long entire_cycles = 256, active_cycles = 192;
	struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
	unsigned int reg_offset, reg_shift, reg_width;
	unsigned int pre_scal_id = 0, div_m = 0, prescale = 0;
	u32 pre_scal[][2] =
	{
		/* reg_value  clk_pre_div */
		{0, 1},
		{1, 2},
		{2, 4},
		{3, 8},
		{4, 16},
		{5, 32},
		{6, 64},
		{7, 128},
		{8, 256},
	};

	reg_offset = pc->config[pwm->pwm - chip->base].reg_bypass_offset;
	reg_shift = pc->config[pwm->pwm - chip->base].reg_bypass_shift;
	reg_width = pc->config[pwm->pwm - chip->base].reg_bypass_width;

	if (period_ns > 0 && period_ns <= 10) {
		/* if freq lt 100M, then direct output 100M clock,set by pass. */
		c = 100000000;
		temp = sunxi_pwm_readl(chip, reg_offset);
		temp = SET_BITS(reg_shift, reg_width, temp, 1);
		sunxi_pwm_writel(chip, reg_offset, temp);

		reg_offset = pc->config[pwm->pwm - chip->base].reg_clk_src_offset;
		reg_shift = pc->config[pwm->pwm - chip->base].reg_clk_src_shift;
		reg_width = pc->config[pwm->pwm - chip->base].reg_clk_src_width;

		temp = sunxi_pwm_readl(chip, reg_offset);
		temp = SET_BITS(reg_shift, reg_width, temp, 1);
		sunxi_pwm_writel(chip, reg_offset, temp);

		return 0;
	} else if (period_ns > 10 && period_ns <= 334) {
		/* if freq between 3M~100M, then select 100M as clock */
		c = 100000000;
		reg_offset = pc->config[pwm->pwm - chip->base].reg_clk_src_offset;
		reg_shift = pc->config[pwm->pwm - chip->base].reg_clk_src_shift;
		reg_width = pc->config[pwm->pwm - chip->base].reg_clk_src_width;

		temp = sunxi_pwm_readl(chip, reg_offset);
		temp = SET_BITS(reg_shift, reg_width, temp, 1);
		sunxi_pwm_writel(chip, reg_offset, temp);
	} else if (period_ns > 334) {
		/* if freq < 3M, then select 24M clock */
		c = 24000000;
		reg_offset = pc->config[pwm->pwm - chip->base].reg_clk_src_offset;
		reg_shift = pc->config[pwm->pwm - chip->base].reg_clk_src_shift;
		reg_width = pc->config[pwm->pwm - chip->base].reg_clk_src_width;

		temp = sunxi_pwm_readl(chip, reg_offset);
		temp = SET_BITS(reg_shift, reg_width, temp, 0);
		sunxi_pwm_writel(chip, reg_offset, temp);
	}
	pwm_debug("%s: duty_ns=%d, period_ns=%d.c =%llu.\n", __func__, duty_ns, period_ns, c);

	c = c * period_ns;
	do_div(c, 1000000000);
	entire_cycles = (unsigned long)c;

	for (pre_scal_id = 0; pre_scal_id < 9; pre_scal_id++) {
		if (entire_cycles <= 65536)
				break;
		for (prescale = 0; prescale < PRESCALE_MAX+1; prescale++) {
			entire_cycles = (entire_cycles/pre_scal[pre_scal_id][1])/(prescale + 1);
			if (entire_cycles <= 65536) {
				div_m = pre_scal[pre_scal_id][0];
				break;
			}
		}
	}

	c = (unsigned long long)entire_cycles * duty_ns;
	do_div(c, period_ns);
	active_cycles = c;
	if (0 == entire_cycles)
		entire_cycles++;

	/* config  clk div_m*/
	reg_offset = pc->config[pwm->pwm - chip->base].reg_clk_div_m_offset;
	reg_shift = pc->config[pwm->pwm - chip->base].reg_clk_div_m_shift;
	reg_width = pc->config[pwm->pwm - chip->base].reg_clk_div_m_width;
	temp = sunxi_pwm_readl(chip, reg_offset);
	temp = SET_BITS(reg_shift, reg_width, temp, div_m);
	sunxi_pwm_writel(chip, reg_offset, temp);

	/* config prescal */
	reg_offset = pc->config[pwm->pwm - chip->base].reg_prescal_offset;
	reg_shift = pc->config[pwm->pwm - chip->base].reg_prescal_shift;
	reg_width = pc->config[pwm->pwm - chip->base].reg_prescal_width;
	temp = sunxi_pwm_readl(chip, reg_offset);
	temp = SET_BITS(reg_shift, reg_width, temp, prescale);
	sunxi_pwm_writel(chip, reg_offset, temp);

	/* config active cycles */
	reg_offset = pc->config[pwm->pwm - chip->base].reg_active_offset;
	reg_shift = pc->config[pwm->pwm - chip->base].reg_active_shift;
	reg_width = pc->config[pwm->pwm - chip->base].reg_active_width;
	temp = sunxi_pwm_readl(chip, reg_offset);
	temp = SET_BITS(reg_shift, reg_width, temp, active_cycles);
	sunxi_pwm_writel(chip, reg_offset, temp);

	/* config period cycles */
	reg_offset = pc->config[pwm->pwm - chip->base].reg_entire_offset;
	reg_shift = pc->config[pwm->pwm - chip->base].reg_entire_shift;
	reg_width = pc->config[pwm->pwm - chip->base].reg_entire_width;
	temp = sunxi_pwm_readl(chip, reg_offset);
	temp = SET_BITS(reg_shift, reg_width, temp, (entire_cycles - 1));

	sunxi_pwm_writel(chip, reg_offset, temp);

	pwm_debug("%s: active_cycles=%lu, entire_cycles=%lu, prescale=%u, div_m=%u\n",
				__func__, active_cycles, entire_cycles, prescale, div_m);
	return 0;
}

static int sunxi_pwm_config_dual(struct pwm_chip *chip, struct pwm_device *pwm,
		int duty_ns, int period_ns, int bind_num)
{
	unsigned int temp;
	unsigned long long c = 0, clk = 0, clk_temp = 0;
	unsigned long entire_cycles = 256, active_cycles = 192;
	unsigned int reg_offset[2], reg_shift[2], reg_width[2];
	unsigned int pre_scal_id = 0, div_m = 0, prescale = 0;
	int src_clk_sel = 0;
	int i = 0;
	unsigned int dead_time = 0, duty = 0;
	u32 pre_scal[][2] =
	{
		/* reg_value  clk_pre_div */
		{0, 1},
		{1, 2},
		{2, 4},
		{3, 8},
		{4, 16},
		{5, 32},
		{6, 64},
		{7, 128},
		{8, 256},
	};
	unsigned int pwm_index[2] = {0};
	struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);

	pwm_index[0] = pwm->pwm - chip->base;
	pwm_index[1] = bind_num - chip->base;

	/* if duty time < dead time,it is wrong. */
	dead_time = pc->config[pwm_index[0]].dead_time;
	duty = (unsigned int)duty_ns;
	/* judge if the pwm eanble dead zone */
	reg_offset[0] = pc->config[pwm_index[0]].reg_dz_en_offset;
	reg_shift[0] = pc->config[pwm_index[0]].reg_dz_en_shift;
	reg_width[0] = pc->config[pwm_index[0]].reg_dz_en_width;

	temp = sunxi_pwm_readl(chip, reg_offset[0]);
	temp &=  (1u << reg_shift[0]);
	if (duty < dead_time || temp == 0) {
		pr_err("[PWM]duty time or dead zone error.\n");
		return -EINVAL;
	}

	for(i = 0; i < PWM_BIND_NUM; i++) {
		reg_offset[i] = pc->config[pwm_index[i]].reg_bypass_offset;
		reg_shift[i] = pc->config[pwm_index[i]].reg_bypass_shift;
		reg_width[i] = pc->config[pwm_index[i]].reg_bypass_width;
	}

	if (period_ns > 0 && period_ns <= 10) {
		/* if freq lt 100M, then direct output 100M clock,set by pass */
		clk = 100000000;
		src_clk_sel = 1;

		/* config the two pwm bypass */
		for(i = 0; i < PWM_BIND_NUM; i++) {
			temp = sunxi_pwm_readl(chip, reg_offset[i]);
			temp = SET_BITS(reg_shift[i], reg_width[i], temp, 1);
			sunxi_pwm_writel(chip, reg_offset[i], temp);

			reg_offset[i] = pc->config[pwm_index[i]].reg_clk_src_offset;
			reg_shift[i] = pc->config[pwm_index[i]].reg_clk_src_shift;
			reg_width[i] = pc->config[pwm_index[i]].reg_clk_src_width;
			temp = sunxi_pwm_readl(chip, reg_offset[i]);
			temp = SET_BITS(reg_shift[i], reg_width[i], temp, 1);
			sunxi_pwm_writel(chip, reg_offset[i], temp);
		}

		return 0;
	} else if (period_ns > 10 && period_ns <= 334) {
		clk = 100000000;
		src_clk_sel = 1;
	} else if (period_ns > 334) {
		/* if freq < 3M, then select 24M clock */
		clk = 24000000;
		src_clk_sel = 0;
	}

	for(i = 0; i < PWM_BIND_NUM; i++) {
		reg_offset[i] = pc->config[pwm_index[i]].reg_clk_src_offset;
		reg_shift[i] = pc->config[pwm_index[i]].reg_clk_src_shift;
		reg_width[i] = pc->config[pwm_index[i]].reg_clk_src_width;

		temp = sunxi_pwm_readl(chip, reg_offset[i]);
		temp = SET_BITS(reg_shift[i], reg_width[i], temp, src_clk_sel);
		sunxi_pwm_writel(chip, reg_offset[i], temp);
	}

	c = clk;
	c *= period_ns;
	do_div(c, 1000000000);
	entire_cycles = (unsigned long)c;

	/* get div_m and prescale,which satisfy: deat_val <= 256, entire <= 65536 */
	for (pre_scal_id = 0; pre_scal_id < 9; pre_scal_id++) {
		for (prescale = 0; prescale < PRESCALE_MAX+1; prescale++) {
			entire_cycles = (entire_cycles/pre_scal[pre_scal_id][1])/(prescale + 1);
			clk_temp = clk;
			do_div(clk_temp, pre_scal[pre_scal_id][1] * (prescale + 1));
			clk_temp *= dead_time;
			do_div(clk_temp, 1000000000);
			if (entire_cycles <= 65536 && clk_temp <= 256) {
				div_m = pre_scal[pre_scal_id][0];
				break;
			}
		}
		if (entire_cycles <= 65536 && clk_temp <= 256)
				break;
		else {
			pr_err("%s:config dual err.entire_cycles=%lu, dead_zone_val=%llu",
					__func__, entire_cycles, clk_temp);
			return -EINVAL;
		}
	}

	c = (unsigned long long)entire_cycles * duty_ns;
	do_div(c,  period_ns);
	active_cycles = c;
	if (0 == entire_cycles)
		entire_cycles++;

	/* config  clk div_m*/
	for(i = 0; i < PWM_BIND_NUM; i++) {
		reg_offset[i] = pc->config[pwm_index[i]].reg_clk_div_m_offset;
		reg_shift[i] = pc->config[pwm_index[i]].reg_clk_div_m_shift;
		reg_width[i] = pc->config[pwm_index[i]].reg_clk_div_m_width;
		temp = sunxi_pwm_readl(chip, reg_offset[i]);
		temp = SET_BITS(reg_shift[i], reg_width[i], temp, div_m);
		sunxi_pwm_writel(chip, reg_offset[i], temp);
	}

	/* config prescal */
	for(i = 0; i < PWM_BIND_NUM; i++) {
		reg_offset[i] = pc->config[pwm_index[i]].reg_prescal_offset;
		reg_shift[i] = pc->config[pwm_index[i]].reg_prescal_shift;
		reg_width[i] = pc->config[pwm_index[i]].reg_prescal_width;
		temp = sunxi_pwm_readl(chip, reg_offset[i]);
		temp = SET_BITS(reg_shift[i], reg_width[i], temp, prescale);
		sunxi_pwm_writel(chip, reg_offset[i], temp);
	}

	/* config active cycles */
	for(i = 0; i < PWM_BIND_NUM; i++) {
		reg_offset[i] = pc->config[pwm_index[i]].reg_active_offset;
		reg_shift[i] = pc->config[pwm_index[i]].reg_active_shift;
		reg_width[i] = pc->config[pwm_index[i]].reg_active_width;
		temp = sunxi_pwm_readl(chip, reg_offset[i]);
		temp = SET_BITS(reg_shift[i], reg_width[i], temp, active_cycles);
		sunxi_pwm_writel(chip, reg_offset[i], temp);
	}

	/* config period cycles */
	for(i = 0; i < PWM_BIND_NUM; i++) {
		reg_offset[i] = pc->config[pwm_index[i]].reg_entire_offset;
		reg_shift[i] = pc->config[pwm_index[i]].reg_entire_shift;
		reg_width[i] = pc->config[pwm_index[i]].reg_entire_width;
		temp = sunxi_pwm_readl(chip, reg_offset[i]);
		temp = SET_BITS(reg_shift[i], reg_width[i], temp, (entire_cycles - 1));
		sunxi_pwm_writel(chip, reg_offset[i], temp);
	}

	pwm_debug("%s: active_cycles=%lu, entire_cycles=%lu, prescale=%u, div_m=%u\n",
				__func__, active_cycles, entire_cycles, prescale, div_m);

	/* config dead zone, one config for two pwm */
	reg_offset[0] = pc->config[pwm_index[0]].reg_pdzintv_offset;
	reg_shift[0] = pc->config[pwm_index[0]].reg_pdzintv_shift;
	reg_width[0] = pc->config[pwm_index[0]].reg_pdzintv_width;
	temp = sunxi_pwm_readl(chip, reg_offset[0]);
	temp = SET_BITS(reg_shift[0], reg_width[0], temp, (unsigned int)clk_temp);
	sunxi_pwm_writel(chip, reg_offset[0], temp);

	return 0;
}

static int sunxi_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
		int duty_ns, int period_ns)
{
	int bind_num;

	struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
	bind_num = pc->config[pwm->pwm - chip->base].bind_pwm;
	if (bind_num == 255)
		sunxi_pwm_config_single(chip, pwm, duty_ns, period_ns);
	else
		sunxi_pwm_config_dual(chip, pwm, duty_ns, period_ns, bind_num);

	return 0;
}

static int sunxi_pwm_enable_single(struct pwm_chip *chip, struct pwm_device *pwm)
{
	unsigned int value = 0, index = 0;
	struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
	unsigned int reg_offset, reg_shift;
	struct device_node *sub_np;
	struct platform_device *pwm_pdevice;

	index = pwm->pwm - chip->base;
	sub_np = of_parse_phandle(chip->dev->of_node, "pwms", index);
	if (IS_ERR_OR_NULL(sub_np)) {
			pr_err("%s: can't parse \"pwms\" property\n", __func__);
			return -ENODEV;
	}
	pwm_pdevice = of_find_device_by_node(sub_np);
	if (IS_ERR_OR_NULL(pwm_pdevice)) {
			pr_err("%s: can't parse pwm device\n", __func__);
			return -ENODEV;
	}
	sunxi_pwm_pin_set_state(&pwm_pdevice->dev, PWM_PIN_STATE_ACTIVE);

	/* enable clk for pwm controller */
	reg_offset = pc->config[pwm->pwm - chip->base].reg_clk_gating_offset;
	reg_shift = pc->config[pwm->pwm - chip->base].reg_clk_gating_shift;
	value = sunxi_pwm_readl(chip, reg_offset);
	value = SET_BITS(reg_shift, 1, value, 1);
	sunxi_pwm_writel(chip, reg_offset, value);

	/* enable pwm controller */
	reg_offset = pc->config[pwm->pwm - chip->base].reg_enable_offset;
	reg_shift = pc->config[pwm->pwm - chip->base].reg_enable_shift;
	value = sunxi_pwm_readl(chip, reg_offset);
	value = SET_BITS(reg_shift, 1, value, 1);
	sunxi_pwm_writel(chip, reg_offset, value);

	return 0;
}

unsigned long long sunxi_get_clk_freq(struct pwm_chip *chip, int pwm)
{
	struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
	unsigned int reg_offset, reg_shift, reg_width;
	unsigned long long c = 0;
	unsigned int temp;
	unsigned int index = 0;
	u32 pre_scal[][2] =
	{
		/* reg_value  clk_pre_div */
		{0, 1},
		{1, 2},
		{2, 4},
		{3, 8},
		{4, 16},
		{5, 32},
		{6, 64},
		{7, 128},
		{8, 256},
	};

	index = pwm - chip->base;
	reg_offset = pc->config[index].reg_clk_src_offset;
	reg_shift = pc->config[index].reg_clk_src_shift;
	reg_width = pc->config[index].reg_clk_src_width;

	temp = sunxi_pwm_readl(chip, reg_offset);
	temp = temp >> reg_shift;
	temp = temp & ((1u << reg_width) - 1);
	if (temp == 0) {
		c = 24000000;
	} else if(temp == 1) {
		c = 100000000;
	}

	/* check if clk is bypass*/
	reg_offset = pc->config[index].reg_bypass_offset;
	reg_shift = pc->config[index].reg_bypass_shift;
	reg_width = pc->config[index].reg_bypass_width;
	temp = sunxi_pwm_readl(chip, reg_offset);
	temp = temp >> reg_shift;
	temp = temp & ((1u << reg_width) - 1);
	if (temp == 1)
		return c;

	/* check clk div m */
	reg_offset = pc->config[index].reg_clk_div_m_offset;
	reg_shift = pc->config[index].reg_clk_div_m_shift;
	reg_width = pc->config[index].reg_clk_div_m_width;
	temp = sunxi_pwm_readl(chip, reg_offset);
	temp = temp >> reg_shift;
	temp = temp & ((1u << reg_width) - 1);
	do_div(c, pre_scal[temp][1]);

	/* check clk prescal */
	reg_offset = pc->config[index].reg_prescal_offset;
	reg_shift = pc->config[index].reg_prescal_shift;
	reg_width = pc->config[index].reg_prescal_width;
	temp = sunxi_pwm_readl(chip, reg_offset);
	temp = temp >> reg_shift;
	temp = temp & ((1u << reg_width) - 1);
	do_div(c, temp + 1);

	return c;
}

static int sunxi_pwm_enable_dual(struct pwm_chip *chip, struct pwm_device *pwm, int bind_num)
{
	u32 value[2] = {0};
	struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
	unsigned int reg_offset[2], reg_shift[2], reg_width[2];
	struct device_node *sub_np[2];
	struct platform_device *pwm_pdevice[2];
	int i = 0;
	unsigned int pwm_index[2] = {0};

	pwm_index[0] = pwm->pwm - chip->base;
	pwm_index[1] = bind_num - chip->base;

	/*set current pwm pin state*/
	sub_np[0] = of_parse_phandle(chip->dev->of_node, "pwms", pwm_index[0]);
	if (IS_ERR_OR_NULL(sub_np[0])) {
			pr_err("%s: can't parse \"pwms\" property\n", __func__);
			return -ENODEV;
	}
	pwm_pdevice[0] = of_find_device_by_node(sub_np[0]);
	if (IS_ERR_OR_NULL(pwm_pdevice[0])) {
			pr_err("%s: can't parse pwm device\n", __func__);
			return -ENODEV;
	}

	/*set bind pwm pin state*/
	sub_np[1] = of_parse_phandle(chip->dev->of_node, "pwms", pwm_index[1]);
	if (IS_ERR_OR_NULL(sub_np[1])) {
			pr_err("%s: can't parse \"pwms\" property\n", __func__);
			return -ENODEV;
	}
	pwm_pdevice[1] = of_find_device_by_node(sub_np[1]);
	if (IS_ERR_OR_NULL(pwm_pdevice[1])) {
			pr_err("%s: can't parse pwm device\n", __func__);
			return -ENODEV;
	}

	sunxi_pwm_pin_set_state(&pwm_pdevice[0]->dev, PWM_PIN_STATE_ACTIVE);
	sunxi_pwm_pin_set_state(&pwm_pdevice[1]->dev, PWM_PIN_STATE_ACTIVE);

	/* enable clk for pwm controller */
	for(i = 0; i < PWM_BIND_NUM; i++) {
		reg_offset[i] = pc->config[pwm_index[i]].reg_clk_gating_offset;
		reg_shift[i] = pc->config[pwm_index[i]].reg_clk_gating_shift;
		reg_width[i] = pc->config[pwm_index[i]].reg_clk_gating_width;
		value[i] = sunxi_pwm_readl(chip, reg_offset[i]);
		value[i] = SET_BITS(reg_shift[i], reg_width[i], value[i], 1);
		sunxi_pwm_writel(chip, reg_offset[i], value[i]);
	}

	/* enable pwm controller */
	for(i = 0; i < PWM_BIND_NUM; i++) {
		reg_offset[i] = pc->config[pwm_index[i]].reg_enable_offset;
		reg_shift[i] = pc->config[pwm_index[i]].reg_enable_shift;
		reg_width[i] = pc->config[pwm_index[i]].reg_enable_width;
		value[i] = sunxi_pwm_readl(chip, reg_offset[i]);
		value[i] = SET_BITS(reg_shift[i], reg_width[i], value[i], 1);
		sunxi_pwm_writel(chip, reg_offset[i], value[i]);
	}

	/* enable pwm dead zone,one for the twice pwm*/
	reg_offset[0] = pc->config[pwm_index[0]].reg_dz_en_offset;
	reg_shift[0] = pc->config[pwm_index[0]].reg_dz_en_shift;
	reg_width[0] = pc->config[pwm_index[0]].reg_dz_en_width;
	value[0] = sunxi_pwm_readl(chip, reg_offset[0]);
	value[0] = SET_BITS(reg_shift[0], reg_width[0], value[0], 1);
	sunxi_pwm_writel(chip, reg_offset[0], value[0]);

	return 0;

}

static int sunxi_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
	int bind_num;
	struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);

	bind_num = pc->config[pwm->pwm - chip->base].bind_pwm;
	if (bind_num == 255)
		sunxi_pwm_enable_single(chip, pwm);
	else
		sunxi_pwm_enable_dual(chip, pwm, bind_num);

	return 0;
}


static void sunxi_pwm_disable_single(struct pwm_chip *chip, struct pwm_device *pwm)
{
	u32 value = 0, index = 0;
	struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
	unsigned int reg_offset, reg_shift, reg_width;
	struct device_node *sub_np;
	struct platform_device *pwm_pdevice;

	index = pwm->pwm - chip->base;
	sub_np = of_parse_phandle(chip->dev->of_node, "pwms", index);
	if (IS_ERR_OR_NULL(sub_np)) {
			pr_err("%s: can't parse \"pwms\" property\n", __func__);
			return;
	}
	pwm_pdevice = of_find_device_by_node(sub_np);
	if (IS_ERR_OR_NULL(pwm_pdevice)) {
			pr_err("%s: can't parse pwm device\n", __func__);
			return;
	}

	/* disable pwm controller */
	reg_offset = pc->config[index].reg_enable_offset;
	reg_shift = pc->config[index].reg_enable_shift;
	reg_width = pc->config[index].reg_enable_width;
	value = sunxi_pwm_readl(chip, reg_offset);
	value = SET_BITS(reg_shift, reg_width, value, 0);
	sunxi_pwm_writel(chip, reg_offset, value);

	/* disable pwm controller */
	reg_offset = pc->config[index].reg_clk_gating_offset;
	reg_shift = pc->config[index].reg_clk_gating_shift;
	reg_width = pc->config[index].reg_clk_gating_width;
	value = sunxi_pwm_readl(chip, reg_offset);
	value = SET_BITS(reg_shift, reg_width, value, 0);
	sunxi_pwm_writel(chip, reg_offset, value);

	sunxi_pwm_pin_set_state(&pwm_pdevice->dev, PWM_PIN_STATE_SLEEP);
}

static void sunxi_pwm_disable_dual(struct pwm_chip *chip, struct pwm_device *pwm, int bind_num)
{
	u32 value[2] = {0};
	struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
	unsigned int reg_offset[2], reg_shift[2], reg_width[2];
	struct device_node *sub_np[2];
	struct platform_device *pwm_pdevice[2];
	int i = 0;
	unsigned int pwm_index[2] = {0};

	pwm_index[0] = pwm->pwm - chip->base;
	pwm_index[1] = bind_num - chip->base;

	/* get current index pwm device */
	sub_np[0] = of_parse_phandle(chip->dev->of_node, "pwms", pwm_index[0]);
	if (IS_ERR_OR_NULL(sub_np[0])) {
			pr_err("%s: can't parse \"pwms\" property\n", __func__);
			return;
	}
	pwm_pdevice[0] = of_find_device_by_node(sub_np[0]);
	if (IS_ERR_OR_NULL(pwm_pdevice[0])) {
			pr_err("%s: can't parse pwm device\n", __func__);
			return;
	}
	/* get bind pwm device */
	sub_np[1] = of_parse_phandle(chip->dev->of_node, "pwms", pwm_index[1]);
	if (IS_ERR_OR_NULL(sub_np[1])) {
			pr_err("%s: can't parse \"pwms\" property\n", __func__);
			return;
	}
	pwm_pdevice[1] = of_find_device_by_node(sub_np[1]);
	if (IS_ERR_OR_NULL(pwm_pdevice[1])) {
			pr_err("%s: can't parse pwm device\n", __func__);
			return;
	}

	/* disable pwm controller */
	for(i = 0; i < PWM_BIND_NUM; i++) {
		reg_offset[i] = pc->config[pwm_index[i]].reg_enable_offset;
		reg_shift[i] = pc->config[pwm_index[i]].reg_enable_shift;
		reg_width[i] = pc->config[pwm_index[i]].reg_enable_width;
		value[i] = sunxi_pwm_readl(chip, reg_offset[i]);
		value[i] = SET_BITS(reg_shift[i], reg_width[i], value[i], 0);
		sunxi_pwm_writel(chip, reg_offset[i], value[i]);
	}

	/* disable pwm clk gating */
	for(i = 0; i < PWM_BIND_NUM; i++) {
		reg_offset[i] = pc->config[pwm_index[i]].reg_clk_gating_offset;
		reg_shift[i] = pc->config[pwm_index[i]].reg_clk_gating_shift;
		reg_width[i] = pc->config[pwm_index[i]].reg_clk_gating_width;
		value[i] = sunxi_pwm_readl(chip, reg_offset[i]);
		value[i] = SET_BITS(reg_shift[i], reg_width[i], value[i], 0);
		sunxi_pwm_writel(chip, reg_offset[i], value[i]);
	}

	/* disable pwm dead zone,one for the two pwm */
	reg_offset[0] = pc->config[pwm->pwm - chip->base].reg_dz_en_offset;
	reg_shift[0] = pc->config[pwm->pwm - chip->base].reg_dz_en_shift;
	reg_width[0] = pc->config[pwm->pwm - chip->base].reg_dz_en_width;
	value[0] = sunxi_pwm_readl(chip, reg_offset[0]);
	value[0] = SET_BITS(reg_shift[0], reg_width[0], value[0], 0);
	sunxi_pwm_writel(chip, reg_offset[0], value[0]);

	/* config pin sleep */
	sunxi_pwm_pin_set_state(&pwm_pdevice[0]->dev, PWM_PIN_STATE_SLEEP);
	sunxi_pwm_pin_set_state(&pwm_pdevice[1]->dev, PWM_PIN_STATE_SLEEP);
}

static void sunxi_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
	int bind_num;
	struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);

	bind_num = pc->config[pwm->pwm - chip->base].bind_pwm;
	if (bind_num == 255)
		sunxi_pwm_disable_single(chip, pwm);
	else
		sunxi_pwm_disable_dual(chip, pwm, bind_num);
}


static struct pwm_ops sunxi_pwm_ops = {
	.config = sunxi_pwm_config,
	.enable = sunxi_pwm_enable,
	.disable = sunxi_pwm_disable,
	.set_polarity = sunxi_pwm_set_polarity,
	.owner = THIS_MODULE,
};

static int sunxi_pwm_probe(struct platform_device *pdev)
{
	int ret;
	struct sunxi_pwm_chip* pwm;
	struct device_node *np = pdev->dev.of_node;
	int i;
	struct platform_device *pwm_pdevice;
	struct device_node *sub_np;

	pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
	if (!pwm) {
		dev_err(&pdev->dev, "failed to allocate memory!\n");
		return -ENOMEM;
	}

	/* io map pwm base */
	pwm->base = (void __iomem *)of_iomap(pdev->dev.of_node, 0);
	if (!pwm->base) {
		dev_err(&pdev->dev, "unable to map pwm registers\n");
		ret = -EINVAL;
		goto err_iomap;
	}

	/* read property pwm-number */
	ret = of_property_read_u32(np, "pwm-number", &pwm->chip.npwm);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get pwm number: %d, force to one!\n", ret);
		/* force to one pwm if read property fail */
		pwm->chip.npwm = 1;
	}

	/* read property pwm-base */
	ret = of_property_read_u32(np, "pwm-base", &pwm->chip.base);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to get pwm-base: %d, force to -1 !\n", ret);
		/* force to one pwm if read property fail */
		pwm->chip.base = -1;
	}
	pwm->chip.dev = &pdev->dev;
	pwm->chip.ops = &sunxi_pwm_ops;

	/* add pwm chip to pwm-core */
	ret = pwmchip_add(&pwm->chip);
	if (ret < 0) {
		dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
		goto err_add;
	}
	platform_set_drvdata(pdev, pwm);

	pwm->config = devm_kzalloc(&pdev->dev, sizeof(*pwm->config) * pwm->chip.npwm, GFP_KERNEL);
	if (!pwm->config) {
		dev_err(&pdev->dev, "failed to allocate memory!\n");
		goto err_alloc;
	}

	for (i=0; i<pwm->chip.npwm; i++) {
		sub_np = of_parse_phandle(np, "pwms",i);
		if (IS_ERR_OR_NULL(sub_np)) {
			pr_err("%s: can't parse \"pwms\" property\n", __func__);
			return -EINVAL;
		}

		pwm_pdevice = of_find_device_by_node(sub_np);
		ret =	sunxi_pwm_get_config(pwm_pdevice, &pwm->config[i]);
		if (ret) {
			goto err_get_config;
		}
	}

  return 0;

err_get_config:
err_alloc:
	pwmchip_remove(&pwm->chip);
err_add:
	iounmap(pwm->base);
err_iomap:
	return ret;
}

static int sunxi_pwm_remove(struct platform_device *pdev)
{
	struct sunxi_pwm_chip *pwm = platform_get_drvdata(pdev);

	return pwmchip_remove(&pwm->chip);
}

static int sunxi_pwm_suspend(struct platform_device *pdev, pm_message_t state)
{
	return 0;
}

static int sunxi_pwm_resume(struct platform_device *pdev)
{
	return 0;
}

#if !defined(CONFIG_OF)
struct platform_device sunxi_pwm_device = {
	.name = "sunxi_pwm",
	.id = -1,
};
#else
static const struct of_device_id sunxi_pwm_match[] = {
	{ .compatible = "allwinner,sunxi-pwm", },
	{ .compatible = "allwinner,sunxi-s_pwm", },
	{},
};
#endif

static struct platform_driver sunxi_pwm_driver = {
	.probe = sunxi_pwm_probe,
	.remove = sunxi_pwm_remove,
	.suspend = sunxi_pwm_suspend,
	.resume = sunxi_pwm_resume,
	.driver = {
		.name = "sunxi_pwm",
		.owner  = THIS_MODULE,
		.of_match_table = sunxi_pwm_match,
	 },
};

static int __init pwm_module_init(void)
{
	int ret = 0;

	pr_info("pwm module init!\n");

#if !defined(CONFIG_OF)
	ret = platform_device_register(&sunxi_pwm_device);
#endif
	if (ret == 0) {
		ret = platform_driver_register(&sunxi_pwm_driver);
	}

	return ret;
}

static void __exit pwm_module_exit(void)
{
	pr_info("pwm module exit!\n");

	platform_driver_unregister(&sunxi_pwm_driver);
#if !defined(CONFIG_OF)
	platform_device_unregister(&sunxi_pwm_device);
#endif
}

subsys_initcall(pwm_module_init);
module_exit(pwm_module_exit);

MODULE_AUTHOR("zengqi");
MODULE_DESCRIPTION("pwm driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:sunxi-pwm");

